{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passive Network Measurement"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Network operators look at different types of network traffic data to understand properties of their networks. Some network data can be collected directly from network devices (e.g., routers, switches) while they are forwarding live traffic.  Collecting this data does not affect network behavior and is therefore called \"passive\" (as opposed to \"active\" measurements).\n",
    "\n",
    "In this assignment, you will analyze two types of passive network measurement data---traffic volumes and BGP routes.\n",
    "\n",
    "This notebook has several parts. Each part contains sections marked with TODO that you need to complete. \n",
    "\n",
    "**Put your name and netID in the cell below:**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Name:张奕鸣**\n",
    "\n",
    "**NetId:10235101501**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Background\n",
    "\n",
    "### Traffic Measurement with IPFIX\n",
    "\n",
    "Routers in most networks collect traffic measurements using the [IPFIX protocol](https://en.wikipedia.org/wiki/IP_Flow_Information_Export). [NetFlow](https://en.wikipedia.org/wiki/NetFlow), a proprietary form of IPFIX defined by Cisco, is well-known in the networks community because Cisco supplies routers for many large networks.\n",
    "\n",
    "In this part of the assignment, you'll analyze a trace of NetFlow records captured from a router that connects the Princeton campus network to the Internet. The assignment will ask you to perform similar kinds of analysis that a network operator would perform -- asking questions about the most popular endpoints for traffic on the Princeton campus, the most popular web applications, and so forth. (As you can imagine, when we start to think about security, the ability to analyze these baselines will come in handy!)\n",
    "\n",
    "The flow records are in the file 'netflow.csv' in the folder.  To simplify the analysis, we have ensured that the IP addresses of the Princeton campus network start with 128.112 and have their 16 lower bits anonymized to protect the privacy of users on the campus network (i.e., all of us!). To further simplify your task, we have parsed these records into CSV (comma-separated variable) format, with the names of the fields listed in the first row of the file. (In a real network, routers export IPFIX records as binary files.)\n",
    "\n",
    "### Measurement of Interdomain Routing with BGP Routing Tables\n",
    "\n",
    "To help network operators understand the state of Internet routing, many routers have the ability to \"dump\" BGP routing tables periodically into a static file. These routing tables contain information about each IP prefix, all BGP routes that the router learns for each prefix, and the \"best\" BGP route that the router ultimately selects. Analyzing the BGP routing tables can provide information about where traffic to different IP prefixes is destined.\n",
    "\n",
    "In this assignment, we have provided you a routing table dump from a project called [RouteViews](http://www.routeviews.org/). You can visit that site to learn more about the routing tables that they collect. `telnet route-views2.routeviews.org` will also give you a command-line prompt at a real BGP router at the routeviews project if you want to play around with a live routing table view.  http://routeviews.org/bgpdata/ has periodic binary routing table dumps and update logs from participating routers. Again, for this assignment, we have parsed the binary routing table dumps into a format that is easier to analyze directly. The data is located in the `bgp_rib.csv` file.\n",
    "\n",
    "### Functional data analysis with map() and reduce()\n",
    "\n",
    "Several of the data analysis steps in this assignment use a \"MapReduce\" programming model. MapReduce originated in functional programming languages and involves using two functions (called `map()` and `reduce()`...surprise!) to apply functions to iterable data (like linked-lists, arrays, etc.). \n",
    "\n",
    "##### map()\n",
    "\n",
    "A general `map()` function has two arguments: another function (which itself takes one argument) and an iterable object. `map()` then applies (maps) the argument function to every item in the iterable object. See the documentation of Python's built-in `map()`function for more details: https://docs.python.org/2/library/functions.html#map. The following toy example uses `map()` to add 3 to every element of a list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "some_numbers = [1,2,3]\n",
    "three_more = map(lambda x: x+3, some_numbers)\n",
    "print three_more"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`map()` is often used with anonymous function (the `lambda` in the above example), but can be used just as easily with normal functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "def add3(i):\n",
    "    return i+3\n",
    " \n",
    "some_numbers = [1,2,3]\n",
    "three_more = map(add3, some_numbers)\n",
    "print three_more"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that real implementations of `map()` allow the mapped function to take more than one argument or include more information in a closure, but this won't be necessary for this assignment.\n",
    "\n",
    "##### reduce()\n",
    "\n",
    "A general `reduce()` function takes another function (which itself takes *two* values), an iterable object, and an optional initializer value.  The argument function is applied to the first two elements in the iterable object (or the first element and the initializer value) to get a return value. The function is then applied to this return value and the next item in the iterable object. This continues through the iterable object until only a single return value remains. This allows `reduce()` to compute summaries over all data in the iterable object. See the documentation of Python's built-in `reduce()` function for more details: https://docs.python.org/2/library/functions.html#reduce. The following example uses `reduce()` to count the number of 4s in a list of integers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    }
   ],
   "source": [
    "def count_4s(count, i):\n",
    "    # The order of the arguments matters. \n",
    "    #     The first argument is the accumulated value\n",
    "    #     The secod argument is next value from the iterable\n",
    "    if i == 4:\n",
    "        return count + 1\n",
    "    else:\n",
    "        return count\n",
    "\n",
    "some_numbers = [1,4,0,1,4]\n",
    "num_fours = reduce(count_4s, some_numbers, 0) # 0 is the initializer value \n",
    "print num_fours"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, real implementations of `reduce()` allow the reduction function to take more than two arguments or include more information in a closure, but this won't be necessary for this assignment.\n",
    "\n",
    "[MapReduce](https://en.wikipedia.org/wiki/MapReduce) is popular because it allows analysis tasks on large data sets to be easily parallelized .  Although there are many open-source and proprietary MapReduce-style data processing libraries (typically with different ways of expressing iterable datasets and distributing tasks over many computers), they all involve `map()` and `reduce()` functions like you will use in this assignment.  If you are interested in learning more about functional programming for data analysis, you should take COS 326."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part A: IPFIX Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Parse IPFIX Data\n",
    "The `netflow.csv` file contains pre-processed netflow data from a Princeton campus network border router. The data is \"unsampled,\" i.e. it compiles flow statistics for every packet that traverses any interface on the border router.  We used the `nfdump` tool to process the raw NetFlow data that the router collected. Each row of the `netflow.csv` file, except for the header on top, logs the following information for a flow:\n",
    "\n",
    "```\n",
    "Date first seen, Time first seen (m:s), Date last seen, Time last seen (m:s), Duration (s), Protocol, \n",
    "Src IP addr, Src port, Dst IP addr, Dst port, Packets, Bytes, Flags, Input interface, Output interface\t\t\n",
    "\n",
    "```\n",
    "\n",
    "To analyze this data, we first need to read it into a python data structure.  The following code uses the built-in `csv` library to read `netflow.csv` into a list of dictionaries.  The `csv` library documentation is here if you are interested: https://docs.python.org/2/library/csv.html\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of flow records: 105360\n",
      "\n",
      "Sample flow record: {'Dst IP addr': '128.112.213.189', 'Protocol': 'ICMP', 'Bytes': '94', 'Src port': '0', 'Src IP addr': '172.16.241.1', 'Input interface': '120', 'Packets': '1', 'Date first seen': '10/29/15', 'Output interface': '0', 'Duration (s)': '0', 'Time first seen (m:s)': '04:48.9', 'Flags': '.A....', 'Time last seen (m:s)': '04:48.9', 'Dst port': '11', 'Date last seen': '10/29/15'}\n"
     ]
    }
   ],
   "source": [
    "import csv\n",
    "\n",
    "with open('netflow.csv', 'r') as netflow_file:\n",
    "    netflow_reader = csv.DictReader(netflow_file)\n",
    "    netflow_data = list(netflow_reader)\n",
    "    \n",
    "print \"Number of flow records: {}\".format(len(netflow_data))\n",
    "print\n",
    "print \"Sample flow record: {}\".format(netflow_data[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Analyze IPFIX Data\n",
    "\n",
    "The following sections each focus on answering a specific question using the netflow data you have prepared. These questions are both of interest to real network operators and might reveal some surprising facts about how we (as a Princton community) use the Internet.  \n",
    "\n",
    "#### What are the most popular IP addresses accessed by users of the Princeton network?\n",
    "\n",
    "In order to answer this question, we have to decide how to measure IP address popularity. Total traffic volume across all flows seems like a reasonable option, but so does total number of flows to an IP address regardless of volume.  Network operaters actually use both metrics (among others), which we will do here as well.  \n",
    "\n",
    "*Step 1: Determine popular IP addresses by number of flows*\n",
    "\n",
    "Complete the following code to produce a python dictionary `ips_by_flows` with counts of the total number of flows to each external (not 128.112.\\*.\\*) IP address in `netflow_data`.  The keys of the dict should be IP addresses and the values should be integer flow counts.\n",
    "\n",
    "First complete the `count_by_flows()` function, which should take an existing dict of the form described above and update it appropriately from `current_flow`.  If you are confused about datatypes, use print statements to inspect variables. \n",
    "\n",
    "You may want to create an additional helper function to test if an IP address starts with 128.112. Helpful string methods are documented here: https://docs.python.org/2/library/stdtypes.html#string-methods.\n",
    "\n",
    "You will then need to use the `reduce()` function to build a dictionary result.  As a hint, the initializer argument to `reduce()` should be `defaultdict(lambda: 0)`. The [defaultdict()](https://docs.python.org/2/library/collections.html#collections.defaultdict)  function creates a dictionary with default values that are the output of the argument function (in this case, just 0). This allows you to increment the value of a particular key without first checking to see if the key is already in the dictionary (if you used `{}` to create the dict instead of `defaultdict()`, this would raise a KeyError).\n",
    "\n",
    "The provided code will print and plot the most popular IPs.  The `check_ips_by_flows()` function will compare the md5 hash of the top 15 most popular IPs your answer against the md5 hash of the correct answer. This will print a message letting you know whether you are correct or need to keep debugging. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Most popular IP addresses by number of flows: [('116.211.0.90', 17015), ('169.54.233.126', 9424), ('163.53.247.3', 2981), ('169.45.161.189', 2494), ('222.186.190.71', 2413)]\n",
      "\n",
      "Hashes match. Your ips_by_flows is correct.\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 576x216 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "from collections import defaultdict\n",
    "from plotting import plot_flows\n",
    "from testing import check_ips_by_flows\n",
    "\n",
    "# TODO: complete count_by_flows function\n",
    "def count_by_flows(counts, current_flow):\n",
    "    # counts is the current dict result指的是目前的字典结果\n",
    "    # current_flow being processed\n",
    "    #current_flow指的是目前检测的那个网址\n",
    "    dst=current_flow['Dst IP addr']\n",
    "    src=current_flow['Src IP addr']\n",
    "    if dst.startswith('128.112.'):\n",
    "        pass\n",
    "    else:\n",
    "        counts[dst]+=1\n",
    "    if src.startswith('128.112.'):\n",
    "        pass\n",
    "    else:\n",
    "        counts[src]+=1\n",
    "    return counts\n",
    "    \n",
    "      \n",
    "\n",
    "# TODO: use reduce() function to apply count_by_flows to netflow_data and assign the result to ips_by_flows\n",
    "\n",
    "# sorted_ips_by_flows = reduce(defaultdict(lamba:0))\n",
    "\n",
    "ips_by_flows=reduce(count_by_flows,netflow_data,defaultdict(lambda:0))\n",
    "# print the top 5 IP addresses by number of flows \n",
    "sorted_ips_by_flows = sorted(ips_by_flows.items(), reverse=True, key=lambda x: x[1])\n",
    "print \"Most popular IP addresses by number of flows: {}\\n\".format(sorted_ips_by_flows[0:5])\n",
    "\n",
    "# check the results\n",
    "check_ips_by_flows(sorted_ips_by_flows[0:15])\n",
    "\n",
    "# plot the results\n",
    "plot_flows(sorted_ips_by_flows)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Step 2: Determine popular IP addresses by total volume*\n",
    "\n",
    "Complete the following code to produce a dict `ips_by_volume` with counts of the total number of bytes to each external (non-Princeton) IP address.  The keys of the dict should be IP addresses and the values should be integer byte counts. Remember that the values in `netflow_data` are strings. You will need to convert them to ints or floats to do arithmetic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Most popular IP addresses by volume: [('212.83.188.161', 928311), ('169.54.233.126', 867928), ('116.211.0.90', 680600), ('169.45.161.189', 229448), ('42.120.221.22', 191958)]\n",
      "\n",
      "Hashes match. Your ips_by_volume is correct.\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 576x216 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "from plotting import plot_volumes\n",
    "from testing import check_ips_by_volume\n",
    "\n",
    "# TODO: complete count_by_volume function\n",
    "def count_by_volume(counts, current_flow):\n",
    "    # counts is the current dict result\n",
    "    # current_flow being processed\n",
    "    dst=(current_flow['Dst IP addr'])\n",
    "    src=current_flow['Src IP addr']\n",
    "    if dst.startswith('128.112'):\n",
    "        pass\n",
    "    else:\n",
    "        bytes=int(current_flow['Bytes'])\n",
    "        counts[dst]+=bytes\n",
    "    if src.startswith('128.112'):\n",
    "        pass\n",
    "    else:\n",
    "        bytes=int(current_flow['Bytes'])\n",
    "        counts[src]+=bytes\n",
    "    return counts\n",
    "\n",
    "        \n",
    "# TODO: use reduce() function to apply count_by_volume to netflow_data and assign the result to ips_by_volume\n",
    "\n",
    "ips_by_volume=reduce(count_by_volume,netflow_data,defaultdict(lambda:0))\n",
    "# print the top 5 IP addresses by volume\n",
    "sorted_ips_by_volume = sorted(ips_by_volume.items(), reverse=True, key=lambda x: x[1])\n",
    "print \"Most popular IP addresses by volume: {}\\n\".format(sorted_ips_by_volume[0:5])\n",
    "\n",
    "# check the results\n",
    "check_ips_by_volume(sorted_ips_by_volume[0:15])\n",
    "\n",
    "# plot the results\n",
    "plot_volumes(sorted_ips_by_volume)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### What are the most popular applications (by protocol) for Princeton network users?\n",
    "\n",
    "What application protocols do you think are the most common on the Princeton network?  Web traffic (HTTP & SSL)? Secure remote connection (SSH)? Email (SMTP, IMAP, POP3)?  In practice, a network operator may want to identify popular applications  to make provisioning plans or change network configurations to treat traffic from different applications differently (e.g., to route traffic on different links). This could prevent high-volume applications, (e.g. video streaming) from interrupting the performance of critical low-volume applications.  \n",
    "\n",
    "You can answer these questions by finding the most popular ports of traffic flows in the netflow data. Many application protocols use well-known fixed ports for their traffic.  For example, HTTP traffic happens on port 80, SSL traffic on port 443, SSH traffic on port 22, SMTP on port 25. \n",
    "\n",
    "Again we will use both number of flows and total traffic volume as metrics for port \"popularity\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Complete the following code to create `ports_by_flows` and `ports_by_volume` dicts from the netflow data.  Use the same strategy as you did above to create `ips_by_flows` and `ips_by_volume`. \n",
    "\n",
    "Include all destination ports and source ports **lower than 1024** (ports lower than 1024 are \"well-known\" and easily mapped to applications). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Most popular ports by number of flows: [('23', 19546), ('53', 6480), ('80', 6352), ('443', 5788), ('0', 4712)]\n",
      "Most popular ports by volume: [('23', 2324852), ('53', 1437375), ('161', 752840), ('0', 509011), ('80', 309148)]\n",
      "\n",
      "Hashes match. Your ports_by_flows is correct.\n",
      "Hashes match. Your ports_by_volume is correct.\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 576x216 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfgAAADTCAYAAABtNAE+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi41LCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvSM8oowAAG1pJREFUeJzt3XuYHVWd7vHvayIgl4RL0IGAJgo6EwERG+QIIogiInLxMgfHERDnwYwwoh4VkKPM6KOj4GHmQVDMSA4XIxcFlFGPBgc8DHOGxA4GSAhoBNQgTkTkjkDgPX/Uatw0+9Ld6b13d/X7eZ79dNWqVbVWVzr966pa9VuyTURERNTLc/rdgYiIiBh/CfARERE1lAAfERFRQwnwERERNZQAHxERUUMJ8BERETWUAB8REVFDCfARERE1lAAfERFRQ9P73YH1MWvWLM+ZM6ff3YiIiOiZZcuW3WN76071JnWAnzNnDoODg/3uRkRERM9I+uVI6uUWfURERA0lwEdERNRQzwO8pO0lXSPpFkkrJZ1Qyj8j6SZJyyUtlrRtr/sWERFRF/24gl8H/A/b84A9geMkzQNOt72L7V2B7wKf6kPfIiIiaqHnAd723bZvKMsPAquA2bYfaKi2CZCJ6iMiIsaor6PoJc0BXgksKeufBY4E7gf2a7HPscCxANNmbM2ck77Xi65GRESMyp2ff0tf2+/bIDtJmwKXAR8aunq3fYrt7YFFwPHN9rO9wPaA7YFpG8/sXYcjIiImkb4EeEnPpQrui2xf3qTKIuDtve1VREREffRjFL2Ac4FVts9oKN+xodqhwK297ltERERd9OMZ/F7Ae4CbJS0vZZ8A3ifpZcBTwC+B+X3oW0RERC30PMDbvg5Qk03f73VfIiIi6iqZ7CIiImqob6/JSboTeBB4Elhne0DSZ6ievz8FrAWOtv2bfvUxIiJispLdn3wyJcAP2L6noWzG0Ctzkj4IzLPd8ln8wMCAM5tcRERMJZKW2R7oVG9C3aJPNruIiIjx0c9MdgYWSzLwVdsLoHM2u2Syi4hO+p1BLGIi6OcV/N62dwPeTDXhzD7QOZtdMtlFRER01rcAb/uu8nUtcAWwx7AqyWYXERExRv1KVbuJpM2GloEDgBXJZhcRETE++vUM/gXAFVXWWqYD37D9A0mXJZtdRETE+utLgLd9O/CKJuW5JR8RETEOJtRrchERETE+uhbgJS2UtFbSimHlfyfpVkkrJZ1WyraSdI2khySd1a0+RURETBXdvEV/HnAWcMFQgaT9qAbPvcL2Y5KeXzb9EfgksFP5jMjOs2cymPddIyIinqVrV/C2rwXuHVb8t8DnbT9W6qwtXx8us8z9sVv9iYiImEp6PcjupcBrS7a6PwIftf2T0RwgmexitJLVLCKmol4PspsObAnsCXwMuFTlXbmRSia7iIiIznod4NcAl7uylOp991k97kNERETt9TrAf5sygYyklwIbAPe03SMiIiJGrWvP4CVdBOwLzJK0BjgVWAgsLK/OPQ4c5TIhfZkffgawgaTDgANs39Kt/kVERNRZ1wK87Xe12PTXLerP6VZfIiIipppksouIiKihCRfgJR0o6TZJqyWd1O/+RERETEb9mk2uKUnTgLOBN1KNuP+JpCtbPYtPJruIiIjmJtoV/B7Aatu3234cuJgqtW1ERESMwoS6ggdmA79uWF8DvLqxQjLZ9VeywkVETA4T7Qq+o2Syi4iI6GyiBfi7gO0b1rcrZRERETEKEy3A/wTYUdJcSRsARwBX9rlPERERk86EegZve52k44EfAtOAhbZX9rlbERERk86ECvAAtr8PfL/f/YiIiJjMJtot+oiIiBgHfQnwkj4saaWkFZIukrRRee6+pGSwu6Q8g4+IiIgxUJnMrXcNSrOB64B5th+VdCnVLfmDqOaKv1jSOcCNtr/S7lgDAwMeHBzsfqcjIiImCEnLbA90qtevW/TTgedJmg5sDNwNvB74Vtl+PnBYn/oWEREx6fV8kJ3tuyR9EfgV8CiwGFgG3Gd7Xam2hiqr3bNMtUx2yRwXERFj0fMreElbUOWXnwtsC2wCHDjS/ZPJLiIiorN+3KJ/A3CH7d/ZfgK4HNgL2LzcsodksIuIiFgv/QjwvwL2lLSxJAH7A7cA1wDvKHWOAr7Th75FRETUQs8DvO0lVIPpbgBuLn1YAJwIfETSamAr4Nxe9y0iIqIuOg6ykzQH+I3txyXtDewCfN32A2Nt1PapwKnDim+nmg8+IiIi1tNIruC/DVjSS4D/DewIfKOrvYqIiIj1MpIA/1QZDPc24Eu2P0yLV9gaSVooaa2kFQ1lp0u6VdJNkq6QtHkp30PS8vK5UdLhY/2GIiIiYgSZ7CQtBU4HPgkcZvt2SSts79Rhv32Ah4ALhupKOgC4uswa9wUA2ydK2hh4vJRvA9wIbNvwXnxTyWQXERFTzXhmsjsG2A84rQT3ucBFnXayfS1w77CyxQ1B+3qq1+Gw/UhD+UZAb/PnRkRE1MxIMtnta/sDQyu275B0/zi0fQxwydCKpFcDC4EXAe9pdfVep0x2yVIXERHdMtIr+OHetz6NSjoFWAcsGiqzvcT2y4HdgZMlbdRs32Syi4iI6KzlFbyk/w4cAbxY0uUNmzYD7htrg5KOBg4G9neTAQC2V0l6CNgJyAP2iIiIMWh3i34p8Huq5+RnN5Q/CPx0LI1JOhD4OPA62480lM8Ffl0G2b0I+HPgzrG0EREREW0CfHnW/iuqWd7+bbQHlnQRsC8wS9IaqsQ2JwMbAldVWWq53vZ8YG/gJElPAE8BH7B9z2jbjIiIiErbQXa2n5Q0TdKM0Waus/2uJsVN08/avhC4cDTHj4iIiNZGMor+fuBGSYuBh4cKbX+ka72KiIiI9TKSAP/d8hkTSdOoBsvdZfvghvIzgWNsb1rW5wPHAU9SJcg51vYtY203IiJiKusY4G2fW+Zp36EUre6UYW6YE4BVwIyhAkkDwBbD6n3D9jll+yHAGcCB7Q688+yZDOZd8oiIiGfp+B68pNcCq6meny8EfiZpr5EcXNJ2wFuArzWUTaNKffvxxrrDnvFvQrLZRUREjNlIbtH/E3DQ0O1ySX9BNSCuYx5c4J+pAvlmDWXHA1favruMpH+apOOAjwAbAK9vdsA6ZLJLBruIiOi2kWSy26DxWbjtVVQBuC1JBwNrbS9rKNsWeCfwpWb72D7b9kuAE4H/2aJOMtlFRER0MJIr+BsknQN8vay/m5ElutkLOETSQVQTyMwAVgKPAavL1fvGklbb3mHYvhcDXxlBGxEREdHESK7g5wO3U91q/3hZfn+nnWyfbHs723OoUt5ebXsL239me04pf2QouEvasWH3twA/H9V3EhEREU9rewUvaWfgJcAVtk/rcl+Ol/QG4AngD8BRXW4vIiKittpNNvMJqlnjbgB2l/Rp2wvH0ojtHwM/blK+acPyCWM5dkRERDxbuyv4dwO72H5Y0tbA96lek4uIiIgJrt0z+MdsPwxg+3cd6rYlaSNJSyXdKGmlpH9oUme+pJslLZd0naR5Y20vIiJiqlOTKdmrDdJ9wNVDq8B+DevYftuIG6mGzG9i+yFJzwWuA06wfX1DnacntCmZ7D5gu20mu4GBAQ8OZsr4iIiYOiQts90xF027W/RvH7Z+1lg74+qviIfK6nPLx8PqJJNdRETEOGk3H/yo54Bvp6SoXUaV0/5s20ua1KldJrtkrYuIiH4Y83P10bL9pO1dge2APSTt1KROMtlFRESMg54F+CG27wOuof1McRcDh/WmRxEREfXTkwAvaWtJm5fl5wFvBG4dVieZ7CIiIsZJu0Q3L7e9sixPBz4K7AGsAP7R9qOjaGcb4PzyHP45wKW2vyvp08Cg7StJJruIiIhx024U/YXAbmX5c1RB+mzgUODLwHtH2ojtm4BXNin/VMNyMtlFRESMk3YBvnGy9gOA3W0/Ielq4MbudisiIiLWR7tn8DMlvVXSoVRzwj8BT7/T/tRYG5S0vaRrJN1SstqdUMpPl3SrpJskXTH0zD4iIiJGr10muwuHFX3M9m8l/Rlwse19x9SgtA2wje0bJG1G9W78YVSvz11te52kLwDYPrHdsZLJLiIippr1zmRn+z0tyn8L7DvWjtm+G7i7LD8oaRUw2/bihmrXA+8YaxsRERFTXdv54FuRtJ/ta9a3cUlzqAbfDc9qdwxwSYt9JlQmu2Sqi4iIiWis78Gfv74NS9oUuAz4UGMeekmnAOuARc32Sya7iIiIztq9B395q03AVuvTaJlR7jJgke3LG8qPBg4G9nerwQERERHRUbtb9PtRJZt5eFi5gNeMtcEydey5wCrbZzSUHwh8HHid7UfGevyIiIhoH+CXAA82e9Yu6Rfr0eZewHuAmyUtL2WfAM4ENgSuqv4G4Hrb89ejnYiIiCmrXYB/c6vb5LbHfAVv+zqemURnyPfHesyIiIh4ppaD7JoF93IbPSIiIia40Y6i/1xXetFA0odLhrsVki6StFG324yIiKib0b4H3+zW+riRNBv4IDDP9qOSLgWOAM5rVn/n2TMZzHvoERERzzLaK/gPdKUXzzQdeF6ZonZj4Dc9aDMiIqJWOl7BS9oQeD+wN2BJA8AC24+Nd2ds3yXpi8CvgEeBxcNS2PY9k10y10VExGQwkiv484FXAf8CfI1qjvj1zmTXjKQtqOabnwtsC2wi6a8b6ySTXURERGcjeQa/i+15DetXSbqlS/15A3CH7d/B09n0XgN8vUvtRURE1NJIruBvlLT70IqkVwE/7VJ/fgXsKWnjkvFuf2BVl9qKiIiorZFcwe8MLJF0e1mfC6yS9FOq1+V3G6/O2F4i6VvADVQTzvwUWDBex4+IiJgqRhLgD+16LxrYPhU4tZdtRkRE1E3HAG/7F5JeDry2FP277ZXd7VZERESsj47P4CUdD3wTeGH5XCppxO/DS5om6aeSvjus/ExJDzWsHy3pd5KWl8/fjPzbiIiIiEYjuUV/LLCH7YcAJH0O+H/Al0fYxglUA+VmDBWUd+m3aFL3EtvHj/C4yWQXERHRwkhG0Qt4vGH9CUaYslbSdsBbqN6fHyqbBpxONfd7REREdEHLK3hJ022vAy6kGkV/Wdl0OCNPdPPPVIF8s4ay44Erbd9d5n1v9HZJ+wA/Az5s+9dN+tWzTHbJWhcREZNVuyv4pQC2T6NKVftI+cy3/cVOB5Z0MLDW9rKGsm2BdwJfarLLvwJzbO8CXEWLPyKSyS4iIqKzds/gn768tr2UEvBHYS/gEEkHARtRPYNfCTwGrC5X7xtLWm17B9u/b9j3a8Bpo2wvIiIiinYBfmtJH2m10fYZ7Q5s+2TgZABJ+wIftX1wYx1JD9neoSxvY/vusukQksEuIiJizNoF+GnApnR5DvgGH5R0CFUGu3uBo3vUbkRERO20C/B32/70eDRi+8fAj5uUb9qw/PQVf0RERKyfdoPsenXlHhEREeOsXYDfv1uNSjpB0gpJKyV9qJSdLulWSTdJukLS5t1qPyIiou5ku7cNSjsBFwN7UCXQ+QEwH3gxcLXtdZK+AGD7xHbHGhgY8ODgYJd7HBERMXFIWmZ7oFO9kWSyG29/ASyx/UhJpPN/gbfZXlzWAa4HtutD3yIiImphJLnox9sK4LOStgIeBQ4Chl+GHwNc0mzn8cpklyx1ERFRZz0P8LZXlVvwi4GHgeXAk0PbJZ1C9arcohb7LwAWAGy4zY69fb4QERExSfTjFj22z7X9Ktv7AH+gyj2PpKOBg4F3u9eDAyIiImqkH7fokfR822slvRB4G7CnpAOpJqZ5ne1H+tGviIiIuuhLgAcuK8/gnwCOs32fpLOADYGrSp76623P71P/IiIiJrW+BHjbr21StkM/+hIREVFHfXkGHxEREd3V1QAv6U5JN0taLmmwlLXMWCfpZEmrJd0m6U3d7FtERESddTWTnaQ7gQHb9zSUHUCTjHWS5gEXUWW42xb4EfBS208++8iVZLKLiIipZsJmsmuTse5Q4GLbj9m+A1hNFewjIiJilLo9yM7AYkkGvlqS1DRqzFg3myrgD1lTyp5hfTPZJYNdRERMBd0O8HvbvkvS86lef7vV9rXQOWNdK8lkFxER0VlXb9Hbvqt8XQtcQbnl3iJj3V3A9g27b1fKIiIiYpS6FuAlbSJps6Fl4ABgRUPGukOGZay7EjhC0oaS5gI7Aku71b+IiIg66+Yt+hcAV5SsdNOBb9j+gaTVNMlYZ3ulpEuBW6hu3R/XbgR9REREtNa1AG/7duAVTcpbZqyz/Vngs93qU0RExFSRTHYRERE11JcAL2mhpLWSVjSUvVPSSklPSer4An9ERES01q/Z5M4DzgIuaChbQTV17FdHepCdZ89kMO+1R0REPEu/ZpO7VtKcYWWrAMrAu4iIiFgP/bqCH7ORZrJLxrqIiJjKJt0gO9sLbA/YHpi28cx+dyciImJCmnQBPiIiIjpLgI+IiKihfr0mdxHwn8DLJK2R9D5Jh0taA/w34HuSftiPvkVERNRBv0bRv6vFpit62pGIiIiayi36iIiIGurmbHLNstV9RtJNkpZLWixp21L+sVK2XNIKSU9K2rJbfYuIiKg7/Wk69nE+sLQP8BBwge2dStkM2w+U5Q8C82zPH7bfW4EP2359pzYGBgY8ODg4/p2PiIiYoCQts90xpXvXruBtXwvcO6zsgYbVTYBmf128C7ioW/2KiIiYCno+yE7SZ4EjgfuB/YZt2xg4EDi+zf7JZBcREdFBzwfZ2T7F9vbAIp4dyN8K/Ifte5+959P7J5NdREREB/0cRb8IePuwsiPI7fmIiIj11tMAL2nHhtVDgVsbts0EXgd8p5d9ioiIqKOuPYMv2er2BWaVDHWnAgdJehnwFPBLoHEE/eHAYtsPd6tPERERU0XXAnyLbHXntql/HnBet/oTERExlSSTXURERA0lwEdERNRQXyabGS87z57JYN53j4iIeJZcwUdERNRQAnxEREQNJcBHRETUUAJ8REREDSXAR0RE1FACfERERA0lwEdERNSQbPe7D2Mm6UHgtn73YwqZBdzT705METnXvZXz3Ts51+vvRba37lRpUie6AW6zPdDvTkwVkgZzvnsj57q3cr57J+e6d3KLPiIiooYS4CMiImposgf4Bf3uwBST8907Ode9lfPdOznXPTKpB9lFREREc5P9Cj4iIiKaSICPiIiooUkb4CUdKOk2SaslndTv/kx0ku6UdLOk5ZIGS9mWkq6S9PPydYtSLklnlnN7k6TdGo5zVKn/c0lHNZS/qhx/ddlX7dqoE0kLJa2VtKKhrG/ntl0bddDifP+9pLvKz/dySQc1bDu5nIvbJL2pobzp7xBJcyUtKeWXSNqglG9Y1leX7XM6tTHZSdpe0jWSbpG0UtIJpTw/35OB7Un3AaYBvwBeDGwA3AjM63e/JvIHuBOYNazsNOCksnwS8IWyfBDwfwABewJLSvmWwO3l6xZleYuybWmpq7Lvm9u1UacPsA+wG7BiIpzbVm3U5dPifP898NEmdeeV3w8bAnPL741p7X6HAJcCR5Tlc4C/LcsfAM4py0cAl7Rro9/naZzO9TbAbmV5M+Bn5fvNz/ck+EzWK/g9gNW2b7f9OHAxcGif+zQZHQqcX5bPBw5rKL/AleuBzSVtA7wJuMr2vbb/AFwFHFi2zbB9vav/gRcMO1azNmrD9rXAvcOK+3luW7VRCy3OdyuHAhfbfsz2HcBqqt8fTX+HlKvH1wPfKvsPP69D5/tbwP6lfqs2Jj3bd9u+oSw/CKwCZpOf70lhsgb42cCvG9bXlLJozcBiScskHVvKXmD77rL8W+AFZbnV+W1XvqZJebs26q6f53aq/v84vtyyXdjwKGi053sr4D7b64aVP+NYZfv9pf6UON/lkcQrgSXk53tSmKwBPkZvb9u7AW8GjpO0T+PG8tdzV9+Z7EUbE1HObU98BXgJsCtwN/C/+tudepG0KXAZ8CHbDzRuy8/3xDVZA/xdwPYN69uVsmjB9l3l61rgCqpbiP81dGurfF1bqrc6v+3Kt2tSTps26q6f53bK/f+w/V+2n7T9FPAv/OkW+WjP9++pbvlOH1b+jGOV7TNL/Vqfb0nPpQrui2xfXorz8z0JTNYA/xNgxzLadQOqAS9X9rlPE5akTSRtNrQMHACsoDpnQ6NZjwK+U5avBI4so1X3BO4vt8p+CBwgaYtyC/QA4Idl2wOS9izPJI8cdqxmbdRdP89tqzZqa9gz2MOpfr6hOhdHlBHwc4EdqQZ1Nf0dUq4UrwHeUfYffl6Hzvc7gKtL/VZtTHrlZ+5cYJXtMxo25ed7Muj3KL+xfqhGUv6MasTqKf3uz0T+UI0UvrF8Vg6dL6rnh/8G/Bz4EbBlKRdwdjm3NwMDDcc6hmoQ0WrgvQ3lA1S/VH8BnMWfsiQ2baNOH+AiqtvCT1A9D3xfP89tuzbq8Glxvi8s3+tNVAFgm4b6p5RzcRtlhHYpb/o7pPx/WVr+Hb4JbFjKNyrrq8v2F3dqY7J/gL2pbo3fBCwvn4Py8z05PklVGxERUUOT9RZ9REREtJEAHxERUUMJ8BERETWUAB8REVFDCfARERE1lAAfUROSttKfZlP7rZ45u9oGTepvKWn+CI47XdJ9Tcp3kLR8FP17jjLzY0TPJMBH1ITt39ve1fauVLOg/dPQuqsJVYbbEugY4MfRc6hmBYuIHkiAj5gCJH1c0ory+btS/HngZeUK//OSZki6WtINZdKWg0dw6OdKuljSKkmXSnqepAMkDc3GhqQ3S/pmaW+z0t4FZdtRkpaWsi+Xq/zpki5UNUf4CkkfHPcTEjEFTO9cJSImM0mvBt4N7E71f36ppB9TXU3vUK74h3KOH2b7AUnPB/4D+G6Hw88D3mf7+hK03w+cCZwlaSvbvwfeCyykmiL0bxra24kqrexrbK+TtIAqZewvgFm2dy71Nh+vcxExleQKPqL+9gYus/2oqzm9vw28tkk9AZ+XdBOwGNhe0qwOx77D1ZzcAF+nmrXwKWAR8FeStgReVY433Buo/ugYLM/yX0c1I9xqqjsLZ0p6E9W0rBExSrmCj4ghR1LNkLZbuaJeQ5V/vZ3hua6H1hdSzUAGcIntJxtmaBsiYKHtTw4/qKRdKFMbA28Hjh35txERkCv4iKng34HDy/PxTYFDS9mDwGYN9WYCa0twfyMwewTHnitp97L8V8B1ALZ/DdxD9RjgvFK2Dp6eahWqCUT+cuguQXkL4IWStqaacOSbwKeA3cb2bUdMbbmCj6g520slXUQ1RSrAV2zfDCBpmaSbge8BZwD/WtaXUs3i1ckq4COSdqWa2WtBw7ZvADNs/6yh7FzgJkmDto+U9A/AjyQ9h2p2uPnAk8C5ZfpQAyeO7TuPmNoym1xEdIWkc4D/tH1+v/sSMRUlwEfEuCuD5v4AvKnFO/gR0WUJ8BERETWUQXYRERE1lAAfERFRQwnwERERNZQAHxERUUMJ8BERETX0/wFipUlMZ9FcEgAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 576x216 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "from plotting import plot_ports\n",
    "from testing import check_ports_by_flows, check_ports_by_volume\n",
    "\n",
    "# TODO: create ports_by_flows and ports_by_volume dicts from netflow_data\n",
    "def count_port_by_flows(counts, current_flow):\n",
    "    # counts is the current dict result指的是目前的字典结果\n",
    "    # current_flow being processed\n",
    "    #current_flow指的是目前检测的那个网址\n",
    "#     Protocol=current_flow['Protocol']\n",
    "    Src_port=(current_flow['Src port'])\n",
    "    Dst_port=(current_flow['Dst port'])\n",
    "    #代码还有问题 dst不太明晰\n",
    "    if float(Src_port)<1024:\n",
    "        counts[str(Src_port)]+=1\n",
    "    if float(Dst_port)<1024:\n",
    "        counts[str(Dst_port)]+=1\n",
    "    return counts\n",
    "\n",
    "def count_port_by_volume(counts, current_flow):\n",
    "    # counts is the current dict result\n",
    "    # current_flow being processed\n",
    "#     Protocol=(current_flow['Protocol'])\n",
    "    bytes=int(current_flow['Bytes'])\n",
    "    Src_port=(current_flow['Src port'])\n",
    "    Dst_port=(current_flow['Dst port'])\n",
    "    #代码还有问题 dat有小数\n",
    "    if float(Src_port)<1024:\n",
    "        counts[str(Src_port)]+=bytes\n",
    "    if float(Dst_port)<1024:\n",
    "        counts[str(Dst_port)]+=bytes\n",
    "    return counts\n",
    "\n",
    "ports_by_flows=reduce(count_port_by_flows,netflow_data,defaultdict(lambda:0))\n",
    "ports_by_volume=reduce(count_port_by_volume,netflow_data,defaultdict(lambda:0))\n",
    "# Print the most popular ports and check the results\n",
    "sorted_ports_by_flows = sorted(ports_by_flows.items(), reverse=True, key=lambda x: x[1])\n",
    "sorted_ports_by_volume = sorted(ports_by_volume.items(), reverse=True, key=lambda x: x[1])\n",
    "print \"Most popular ports by number of flows: {}\".format(sorted_ports_by_flows[0:5])\n",
    "print \"Most popular ports by volume: {}\\n\".format(sorted_ports_by_volume[0:5])\n",
    "check_ports_by_flows(sorted_ports_by_flows[0:15])\n",
    "check_ports_by_volume(sorted_ports_by_volume[0:15])\n",
    "\n",
    "# plot the results \n",
    "plot_ports(sorted_ports_by_flows, sorted_ports_by_volume)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Questions\n",
    "\n",
    "Answer the following questions about the results of the above analysis. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Q1. \n",
    "What are the 5 most popular external (non-Princeton) IP addresses by number of flows and by traffic volume?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A1.\n",
    "*TODO: Your answer here.*\n",
    "可以通过表格得出， 如果是按照流量数量：\n",
    "116.211.0.90，\n",
    "169.54.233.126,\n",
    "163.53.247.3,\n",
    "169.45.161.189,\n",
    "222.186.190.71, \n",
    "如果是按照流量：\n",
    "212.83.188.161, \n",
    "169.54.233.126,\n",
    "116.211.0.90,\n",
    "169.45.161.189,\n",
    "42.120.221.22"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Q2. \n",
    "Use the \"whois\" command from your Vagrant terminal to learn what you can about these IP addresses (e.g. `whois 169.54.233.126`).  Choose 2 addresses from your answer to Q1 and write up what you learned, as well as why you think they were among the most popular. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A2. \n",
    "*TODO: Your answer here.*\n",
    "以169.54.233.126为例（其实169.53.0.0到169.63.255.255都属于这个板块），他的公司名称是SoftLayer Technologies, Inc.SoftLayer是一家提供全球云基础设施和托管服务的公司，于2013年被IBM收购，现已成为IBM云计算服务的一部分。或许这意味着很多计算机专业同学利用这一个网络进行数据的云端计算。\n",
    "163.53.247.3也很有意思。China Law Building, 27 Andar A, MACAO描述字段提供了这个网络块的物理位置，位于澳门的中国法律大厦27楼A座。这或许意味着很多外国留学生需要法律援助，所以这个网页被经常访问。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Q3. \n",
    "What are the 5 most popular ports by number of flows and by traffic volume? What applications are associated them? There is a wikipedia page of fixed ports here: https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers. You can also search online to find fixed port/application mappings.  Are you suprised by which applications are the most popular?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A3.\n",
    "*TODO: Your answer here.*\n",
    "by number of flows: 23, 53,80,443, 0, \n",
    "by volume:23，53，161，0，80 \n",
    "23：\tTelnet（远程登录）协议加密文本通信\n",
    "53：\t域名系统\n",
    "80：超文本传输协议（HTTP）\n",
    "161：简单网络管理协议（SNMP）\n",
    "443：超文本传输协议安全（HTTPS）\n",
    "0：在编程API（不在主机之间通信）中，请求系统分配的（动态）端口\n",
    "最常用的是远程登录系统。这并不让人惊讶，很有可能是一种远端接入校园网的验证加密方式。还有类似http，https等，这些是最常用的网络协议，理论上他们应该更靠前一些。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Q4. \n",
    "Why do you think that telnet (port 23) composes so much of the traffic through Princeton's network?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A4. \n",
    "*TODO: Your answer here.*\n",
    "23端口或许是远程登录普林斯顿校园网时需要的加密通信，这或许意味着同学们有相当一部分处在校外登录校园网，导致了大量的23端口通信"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Q5. \n",
    "The provided NetFlow data was captured over a 5 minute period from approximately 6:05am to 6:10am. How much do you think the capture time affected the resulting most popular applications?  What changes would you expect to see if the data had been captured during a different 5 minute window (of your choice)? "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A5.\n",
    "*TODO: Your answer here.*\n",
    "或许在晚一些的时间（例如早上10点左右），23端口的访问会减少（同学们来到校园，不需要远程登录），同时80端口与443端口访问增多（更多同学开始到互联网查找资料）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part B: Analyze BGP Routing Tables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Up until this point in the assignment, we have looked at top traffic flows at the level of IP addresses and ports, but a network operator might also be interested in exploring which other networks (i.e., autonomous systems) are responsible for sending or receiving traffic to the network. From our previous lectures on peering and Internet business relationships, it should be clear why an operator might care about knowing which ASes are sending traffic its way! This information may also be useful for exploring various kinds of network attacks (e.g., sources of denial of service attacks), which we will explore in the next assignment.\n",
    "\n",
    "The RouteViews project allows network operators to obtain real-time information about the global routing system from the perspectives of several different autonomous systems around the Internet. RouteViews servers act as software BGP routers, obtaining their BGP routing information via BGP sessions, just like any other router would learn BGP routes. The main difference between the RouteViews servers and other BGP-speaking routers is that the RouteViews servers do not forward any real Internet traffic.\n",
    "\n",
    "RouteViews periodically logs BGP routing tables (sometimes called a Routing Information Base, or a \"RIB\") in a binary format called MRT. We collected data from one such server and used the `bgpdump` tool to parse the data into a more parsable output format. The entries in the BGP RIB table look like the ones shown below: \n",
    "```\n",
    "TIME: 03/07/16 02:00:00\n",
    "TYPE: TABLE_DUMP_V2/IPV4_UNICAST\n",
    "PREFIX: 0.0.0.0/0\n",
    "SEQUENCE: 0\n",
    "FROM: 185.44.116.1 AS47872\n",
    "ORIGINATED: 03/06/16 20:27:05\n",
    "ORIGIN: IGP\n",
    "ASPATH: 47872 3356\n",
    "NEXT_HOP: 185.44.116.1\n",
    "COMMUNITY: 3356:2 3356:514 3356:2087 47872:1 47872:3356\n",
    "\n",
    "TIME: 03/07/16 02:00:00\n",
    "TYPE: TABLE_DUMP_V2/IPV4_UNICAST\n",
    "PREFIX: 0.0.0.0/0\n",
    "SEQUENCE: 0\n",
    "FROM: 80.241.176.31 AS20771\n",
    "ORIGINATED: 03/04/16 10:21:21\n",
    "ORIGIN: IGP\n",
    "ASPATH: 20771 1299\n",
    "NEXT_HOP: 80.241.176.31\n",
    "```\n",
    "\n",
    "BGP RIBs might have multiple entries for an IP prefix. \n",
    "\n",
    "For this assignment, we considered only a single entry for an IP prefix. We translated this data into the `bgp_rib.csv` file. Each contains the following fields:\n",
    "```\n",
    "TIME, ORIGIN, FROM, SEQUENCE, ASPATH, PREFIX, NEXT_HOP\n",
    "```\n",
    "\n",
    "The following code imports the `bgp_rib.csv` file into a list of dicts (like we did for `netflow_data` above)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of BGP RIBs: 208272\n",
      "\n",
      "Sample BGP RIB: {'ORIGIN': 'IGP', 'FROM': '80.249.208.200 AS12859', 'SEQUENCE': '0', 'ASPATH': '12859 3257 4637 1221 38803 56203', 'PREFIX': '1.0.4.0/24', 'NEXT_HOP': '80.249.208.200', 'TIME': '03/07/16 00:00:00'}\n"
     ]
    }
   ],
   "source": [
    "import csv\n",
    "\n",
    "with open('bgp_rib.csv', 'r') as bgp_file:\n",
    "    bgp_reader = csv.DictReader(bgp_file,delimiter=\";\")\n",
    "    bgp_data = list(bgp_reader)\n",
    "    \n",
    "print \"Number of BGP RIBs: {}\".format(len(bgp_data))\n",
    "print\n",
    "print \"Sample BGP RIB: {}\".format(bgp_data[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### What is the longest route (by number of unique ASes) in the BGP RouteViews data?\n",
    "\n",
    "To answer this question, you will need to sort `bgp_data` by the number of **unique** AS numbers in the \"ASPATH\" field.  \n",
    "\n",
    "Complete the code below to find the longest ASPATH and assign it to the `longest_aspath` variable.  You can use Python's built-in `sorted()` function (https://docs.python.org/2/library/functions.html#sorted) or another method of your choosing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The longest ASPATH is: 20562 9002 9198 12997 51346 24722 8449 3356 38193 55330 131284 131284 131284 131284 131284 131284 131284 131284 131284 131284 58630\n",
      "\n",
      "Hashes match. Your longest_aspath is correct.\n"
     ]
    }
   ],
   "source": [
    "from testing import check_longest_aspath\n",
    "\n",
    "longest_aspath=0\n",
    "lengths=[]\n",
    "paths={}\n",
    "# TODO: Find the longest (by number of *unique* AS numbers) ASPATH and assign it to the 'longest_aspath' variable\n",
    "for i in bgp_data:\n",
    "    aspath=i['ASPATH']\n",
    "    aspath_len =len(set(aspath.split()))\n",
    "    lengths.append(aspath_len)\n",
    "    paths[aspath_len]=aspath\n",
    "#     print(aspaths)\n",
    "longest_aspath=paths[sorted(lengths,reverse=True)[0]]\n",
    "# print and check the longest ASPATH\n",
    "print \"The longest ASPATH is: {}\\n\".format(longest_aspath)\n",
    "check_longest_aspath(str(longest_aspath))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Questions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Q6.\n",
    "If you search online for \"AS number lookup\" you will find several AS search services. Look up the ASes in this longest route to find their countries of origin. List these countries in order."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A6. \n",
    "*TODO: Your answer here.*\n",
    "这些国家分别是：荷兰 英国 哈萨克斯坦 吉尔吉斯斯坦 塔吉克斯坦 塔吉克斯坦 吉尔吉斯斯坦 美国 巴基斯坦 阿富汗 阿富汗 阿富汗 阿富汗 阿富汗 阿富汗 阿富汗 阿富汗 阿富汗 阿富汗 阿富汗。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Q7. \n",
    "Why might such long BGP routes be a concern for network operators? Give at least 2 reasons."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### A7.\n",
    "*TODO: Your answer here.*\n",
    "1.这说明了BGP并没有做到选择最短的路径，这样一条路径是非常费时间也占用网络资源的。优化这条路径可以使得网络更加顺畅。\n",
    "2.长距离的网络通信使得丢包率大大上升，只要路径当中一个点出现问题就会影响整条路径。优化这条路径能够带来更稳定的网络环境。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Submission\n",
    "\n",
    "**Remember to \"Save and Checkpoint\" (from the \"File\" menu above) before you leave the notebook or close your tab.**"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
